package com.portal.signature.utils;

import cn.hutool.core.map.MapUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.apache.tomcat.util.codec.binary.Base64;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * 加签工具类
 *
 * @author Amadeus
 * @date 2022/07/04
 */
public class SignatureUtils {

    /**
     * 私钥
     */
    private static final String PRIVATE_KEY = "MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAwc3fvHH6qK76ixWL\n" +
            "ms2bM3G/GPY9w4wWh8/ucblp6u4wV9t/QtIzj5hOLIfyHTn9Lo83VKVmY2PVceld\n" +
            "OXtKMQIDAQABAkAVx5IeNlzp+ggT+07eDb8kT+Me5gVY5XRevOJaC3E4wmuj/e8T\n" +
            "hrI0CPWAOD18lSD19bIa8heSv0MTqU13kvvFAiEA7oaQlr0utEv02hFKNIvKQwqV\n" +
            "xZ+QyGyGCX4zoySLqKsCIQDQAJRi7V4O3OuGaw6MSTh55x+SkSrqDq6Zn6ZBMbxQ\n" +
            "kwIgF386Dx9j0m8JF9JMNRDwQ9pvFqC5aoUGTzkXbSLUfzECIQCHN5k/Jmf66YsC\n" +
            "+x2d/QuF08YmldB8D8dZfvupbhepmQIhAN/7QuncQYjWELUd4KoaUtqhYnrd/zLe\n" +
            "s1Ifox9YNHl8";

    /**
     * 公钥
     */
    private static final String PUBLIC_KEY  = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMHN37xx+qiu+osVi5rNmzNxvxj2PcOM\n" +
            "FofP7nG5aeruMFfbf0LSM4+YTiyH8h05/S6PN1SlZmNj1XHpXTl7SjECAwEAAQ==";

    /**
     * 当地的url
     */
    private static final String LOCAL_URL = "http://localhost:8080/service/123";

    static class GsonUtil {
        static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();

        public static String toJsonString(Object obj) {
            return GSON.toJson(obj);
        }

        public static Map<String, Object> fromJson2Map(String obj) {
            TypeToken<Map<String, Object>> mapTypeToken = new TypeToken<Map<String, Object>>() {};
            return fromJson2Type(obj, mapTypeToken);
        }

        public static <T> T fromJson2Type(String obj, TypeToken typeToken) {
            return GSON.fromJson(obj, typeToken.getType());
        }
    }

    /**
     * 合作方返回结果时，会用我方公钥加密
     * 我们收到返回结果时，用我方私钥解密
     */
    private static String decryptByPriKey(String resp) {
        RSA rsa = new RSA(PRIVATE_KEY, null);
        Map<String, Object> map1 = GsonUtil.fromJson2Map(resp);
        return rsa.decryptStr(map1.get("bizContent").toString(), KeyType.PrivateKey);
    }

    /**
     * 合作方返回结果时，会用合作方私钥加签
     * 我们收到返回结果时，用合作方公钥验签
     */
    private static boolean checkSignByPubKey(Map<String, Object> respMap) {
        String signValue = (String) respMap.get("sign");
        respMap.remove("sign");
        String s = GsonUtil.toJsonString(respMap);
        Sign sign = new Sign(SignAlgorithm.MD5withRSA, null, PUBLIC_KEY);
        return sign.verify(s.getBytes(StandardCharsets.UTF_8), Base64.decodeBase64(signValue.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 合作方公钥加密
     * 合作方收到请求后，用合作方私钥解密
     */
    private static Object encryptedByPubKey(Map<String, Object> bizMap) {
        TreeMap<String, Object> sort = MapUtil.sort(bizMap);
        String sortJson = GsonUtil.toJsonString(sort);
        RSA pubRsa = new RSA(null, PUBLIC_KEY);
        byte[] encryptedData = pubRsa.encrypt(sortJson.getBytes(StandardCharsets.UTF_8), KeyType.PublicKey);
        return Base64.encodeBase64String(encryptedData);
    }

    /**
     * 己方私钥加签
     * 合作方收到请求后，用我方公钥验签
     */
    public static String signByPriKey(Map<String, Object> params) {
        TreeMap<String, Object> sort = MapUtil.sort(params);
        String paramsJson = GsonUtil.toJsonString(sort);
        Sign sign = new Sign(SignAlgorithm.MD5withRSA, PRIVATE_KEY, null);
        return Base64.encodeBase64String(sign.sign(paramsJson.getBytes(StandardCharsets.UTF_8)));
    }


    public static void main(String[] args) {
        // 业务参数
        Map<String, Object> bizMap = new HashMap<>();
        bizMap.put("mobile", "15311281112");
        bizMap.put("certNo", "130625199210101010");
        // 网关请求
        HashMap<String, Object> map = new HashMap<>();
        map.put("appKey", "xxxxxxxxxx");
        map.put("bizContent", encryptedByPubKey(bizMap));
        map.put("signType", "RSA");
        map.put("sign", signByPriKey(map));
        // 发送请求
        System.out.println("请求入参：" + GsonUtil.toJsonString(map));
        String resp = HttpUtil.createPost(LOCAL_URL)
                .body(GsonUtil.toJsonString(map), ContentType.JSON.toString())
                .execute()
                .body();
        System.out.println("请求出参：" + resp);
        // 验签
        if (!checkSignByPubKey(GsonUtil.fromJson2Map(resp))) {
            System.out.println("签名错误");
            return;
        }
        // 解密
        String bizContents = decryptByPriKey(resp);
        System.out.println("解密：" + bizContents);
    }

}


